﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LogicBaseLibrary.Zip
{
    public class cZip
    {
        zipHeader header = new zipHeader();
        FileStream fs_out = null;
        FileStream fstmpout = null;
        FileStream fs_in = null;
        FileStream fstmpin = null;

        string rootPath = string.Empty;
        string saveFile = string.Empty;
        string openFile = string.Empty;

        public cZip(string zipPath, string saveFile)
        {
            this.rootPath = zipPath;
            this.saveFile = saveFile;
        }
        public cZip(string zipFile)
        {
            this.openFile = zipFile;
        }
        public void Compress()
        {
            string tempfile = Path.GetTempFileName();
            FileInfo fi = new FileInfo(this.saveFile);
            if (fi.Exists)
            {
                fi.Delete();
            }

            fs_out = fi.Create();

            fstmpout = new FileStream(tempfile, FileMode.Create);

            CompressPath(this.rootPath);

            byte[] headerBuffer = header.ToByte();

            //fs_out.Seek(0, SeekOrigin.Begin);
            fs_out.Write(headerBuffer, 0, headerBuffer.Length);

            byte[] buffer = new byte[102400];

            int readCount = fstmpout.Read(buffer, 0, buffer.Length);
            while (readCount > 0)
            {
                fs_out.Write(buffer, 0, readCount);
                readCount = fstmpout.Read(buffer, 0, buffer.Length);
            }

            fs_out.Flush();
            fs_out.Close();
            fstmpout.Close();
            fstmpout.Dispose();
            File.Delete(tempfile);
        }
        
        public void DeCompress(string extractPath)
        {
            try
            {
                if (File.Exists(this.openFile))
                {
                    fs_in = new FileStream(this.openFile, FileMode.Open);
                    zipHeader header = ReadHeader();
                    if (header == null)
                        return;

                    foreach (FileItem fi in header.FileItems)
                    {
                        long total = 0;
                        int readcount = 0;
                        string path = Path.Combine(fi.Path, fi.Name);
                        string pathFile = Path.Combine(extractPath, path.TrimStart(new char[] { '\\' }));
                        string tmpfile = Path.GetTempFileName();
                        byte[] buffer = new byte[102400];

                        fs_out = new FileStream(tmpfile, FileMode.Create, FileAccess.Write);

                        int needByte = 0;
                        while (total < fi.CompressLength)
                        {
                            needByte = (fi.CompressLength - total) > (long)buffer.Length ? buffer.Length : (int)(fi.CompressLength - total);
                            readcount = fs_in.Read(buffer, 0, needByte);
                            total += readcount;
                            fs_out.Write(buffer, 0, readcount);
                        }
                        fs_out.Close();
                        fs_out.Dispose();
                        fstmpin = new FileStream(tmpfile, FileMode.Open, FileAccess.Read);
                        Compression.DeCompress(fstmpin, pathFile);
                        fstmpin.Close();
                        fstmpin.Dispose();
                        File.Delete(tmpfile);
                    }
                }
            }
            catch { }
            finally
            {
                if (fs_in != null)
                    fs_in.Close();
                if (fs_out != null)
                    fs_out.Close();
                if (fstmpin != null)
                    fstmpin.Close();
            }
        }

        public string GetFiles()
        {
            StringBuilder sb = new StringBuilder();
            if (File.Exists(this.openFile))
            {
                fs_in = new FileStream(this.openFile, FileMode.Open);
                zipHeader header = this.ReadHeader();
                foreach (FileItem fi in header.FileItems)
                {
                    sb.AppendFormat("{0},{1},{2}byte.\r", fi.Name, fi.Path, fi.CompressLength);
                }
                fs_in.Close();
            }
            return sb.ToString();
        }

        public void Close()
        {
            
        }
        
        private zipHeader ReadHeader()
        {
            zipHeader fheader = null;
            if (fs_in != null)
            {
                byte[] buffer = new byte[10];
                int readcount = fs_in.Read(buffer, 0, 3);
                if (readcount != 3 || !CheckHeader(buffer))
                {
                    throw new Exception("File format error!");
                }

                readcount = fs_in.Read(buffer, 0, 4);
                if (readcount == 4)
                {
                    int headersize = BitConverter.ToInt32(buffer, 0) - 7;
                    if (headersize > 0)
                    {
                        buffer = new byte[headersize];
                        readcount = fs_in.Read(buffer, 0, buffer.Length);
                        
                        fheader = zipHeader.FromeByte(buffer);
                    }
                }
            }
            return fheader;
        }

        private bool CheckHeader(byte[] buffer)
        {
            if( buffer.Length<3)
                return false;

            return (buffer[0] == 64 && buffer[1] == 67 && buffer[2] == 90);
        }

        private void CompressPath(string path)
        {
            string[] files = Directory.GetFiles(path);
            FileStream fs = null;

            MemoryStream ms = null;

            foreach (string zipFile in files)
            {
                FileInfo finfo = new FileInfo(zipFile);
                fs = finfo.OpenRead();
                string name = finfo.Name;
                string fpath = finfo.DirectoryName.Replace(this.rootPath, "\\");
                long unCompressLength = finfo.Length;
                long compressLength = 0;

                ms = (MemoryStream)Compression.Compress(fs);

                compressLength = ms.Length;


                //xfstmpout = new FileStream(Path.Combine("f:\\tmp", finfo.Name), FileMode.Create);
                byte[] buffer = new byte[102400];
                ms.Seek(0, SeekOrigin.Begin);
                int readCount = ms.Read(buffer, 0, buffer.Length);
                int total = 0;
                while (readCount > 0)
                {
                    //fs_out.Write(buffer, 0, readCount);
                    fstmpout.Write(buffer, 0, readCount);
                    total += readCount;
                    readCount = ms.Read(buffer, 0, buffer.Length);
                }
                ms.Close();
                ms.Dispose();

                FileItem fi = new FileItem(name, fpath, compressLength, unCompressLength);
                this.header.AddFile(fi);
            }
            fstmpout.Flush();
            fstmpout.Seek(0, SeekOrigin.Begin);
        }


    }

}
